package com.sprite.framework.entity.mapper.reolver;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.sprite.framework.entity.mapper.MapperException;
import com.sprite.utils.cache.UtilSoftCache;

import ognl.MemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public final class OgnlUtils {

	private static UtilSoftCache<Object> _cahce = UtilSoftCache.createUtilCache("");
	
	private static DefaultMemberAccess memberAccess = new DefaultMemberAccess(true);
	
	private static Object pressExpression(String expression) throws OgnlException{
		Object object = _cahce.get(expression);
		if(object != null){
			return object;
		}

		object = Ognl.parseExpression(expression);

		_cahce.put(expression, object);

		return object;
	}

	public static Object evaluateObject (String expression, Object parameterObject){
		try {
			Object prs = pressExpression(expression);
			Object value = Ognl.getValue(prs, new OgnlContext(null, null, memberAccess),parameterObject);
			return value;
		} catch (Exception e) {
			throw new MapperException(e);
		}
	}

	public static boolean evaluateBoolean(String expression, Object parameterObject) {
		Object value = evaluateObject(expression, parameterObject);
		if (value instanceof Boolean) {
			return (Boolean) value;
		}
		if (value instanceof Number) {
			return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);
		}
		return value != null;

	}

	public static Iterable<?> evaluateIterable(String expression, Object parameterObject) {
		Object value = evaluateObject(expression, parameterObject);
		if (value == null) {
			throw new MapperException("The expression '" + expression + "' evaluated to a null value.");
		}
		if (value instanceof Iterable) {
			return (Iterable<?>) value;
		}
		if (value.getClass().isArray()) {
			int size = Array.getLength(value);
			List<Object> answer = new ArrayList<Object>();
			for (int i = 0; i < size; i++) {
				Object o = Array.get(value, i);
				answer.add(o);
			}
			return answer;
		}
		if (value instanceof Map) {
			return ((Map<?,?>) value).entrySet();
		}

		throw new MapperException("Error evaluating expression '" + expression + "'.  Return value (" + value + ") was not iterable.");
	}

	/**
	 * 来自
	 * <a href="https://github.com/jkuhnert/ognl/blob/master/src/test/java/ognl/DefaultMemberAccess.java">https://github.com/jkuhnert/ognl/blob/master/src/test/java/ognl/DefaultMemberAccess.java</a>
	 */
	public static class DefaultMemberAccess implements MemberAccess{
		public boolean      allowPrivateAccess = false;
		public boolean      allowProtectedAccess = false;
		public boolean      allowPackageProtectedAccess = false;

		/*===================================================================
				Constructors
			  ===================================================================*/
		public DefaultMemberAccess(boolean allowAllAccess)
		{
			this(allowAllAccess, allowAllAccess, allowAllAccess);
		}

		public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
		{
			super();
			this.allowPrivateAccess = allowPrivateAccess;
			this.allowProtectedAccess = allowProtectedAccess;
			this.allowPackageProtectedAccess = allowPackageProtectedAccess;
		}

		/*===================================================================
				Public methods
			  ===================================================================*/
		public boolean getAllowPrivateAccess()
		{
			return allowPrivateAccess;
		}

		public void setAllowPrivateAccess(boolean value)
		{
			allowPrivateAccess = value;
		}

		public boolean getAllowProtectedAccess()
		{
			return allowProtectedAccess;
		}

		public void setAllowProtectedAccess(boolean value)
		{
			allowProtectedAccess = value;
		}

		public boolean getAllowPackageProtectedAccess()
		{
			return allowPackageProtectedAccess;
		}

		public void setAllowPackageProtectedAccess(boolean value)
		{
			allowPackageProtectedAccess = value;
		}

		/*===================================================================
				MemberAccess interface
			  ===================================================================*/
		public Object setup(Map context, Object target, Member member, String propertyName)
		{
			Object      result = null;

			if (isAccessible(context, target, member, propertyName)) {
				AccessibleObject    accessible = (AccessibleObject) member;

				if (!accessible.isAccessible()) {
					result = Boolean.FALSE;
					accessible.setAccessible(true);
				}
			}
			return result;
		}

		public void restore(Map context, Object target, Member member, String propertyName, Object state)
		{
			if (state != null) {
				final AccessibleObject  accessible = (AccessibleObject) member;
				final boolean           stateboolean = ((Boolean) state).booleanValue();  // Using twice (avoid unboxing)
				if (!stateboolean) {
					accessible.setAccessible(stateboolean);
				} else {
					throw new IllegalArgumentException("Improper restore state [" + stateboolean + "] for target [" + target +
							"], member [" + member + "], propertyName [" + propertyName + "]");
				}
			}
		}

		/**
		        Returns true if the given member is accessible or can be made accessible
		        by this object.
		 */
		public boolean isAccessible(Map context, Object target, Member member, String propertyName)
		{
			int         modifiers = member.getModifiers();
			boolean     result = Modifier.isPublic(modifiers);

			if (!result) {
				if (Modifier.isPrivate(modifiers)) {
					result = getAllowPrivateAccess();
				} else {
					if (Modifier.isProtected(modifiers)) {
						result = getAllowProtectedAccess();
					} else {
						result = getAllowPackageProtectedAccess();
					}
				}
			}
			return result;
		}
	}
}
